"""
数据验证和事务管理
"""

from typing import Any, Dict, List, Optional, Type, Union
from pydantic import BaseModel, ValidationError as PydanticValidationError
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import text
from contextlib import asynccontextmanager
import logging
import asyncio
from enum import Enum

from .database import DatabaseOperation
from ..core.exceptions import ValidationError, DatabaseError
from ..core.logging import get_logger

logger = get_logger(__name__)


class ValidationRule:
    """验证规则"""
    
    def __init__(self, name: str, validator, message: str = None):
        self.name = name
        self.validator = validator
        self.message = message or f"Validation failed for {name}"


class ValidationEngine:
    """验证引擎"""
    
    def __init__(self):
        self.rules: Dict[str, List[ValidationRule]] = {}
    
    def add_rule(self, field: str, rule: ValidationRule):
        """添加验证规则"""
        if field not in self.rules:
            self.rules[field] = []
        self.rules[field].append(rule)
    
    def remove_rule(self, field: str, rule_name: str):
        """移除验证规则"""
        if field in self.rules:
            self.rules[field] = [
                rule for rule in self.rules[field] 
                if rule.name != rule_name
            ]
    
    async def validate(self, data: Dict[str, Any]) -> Dict[str, List[str]]:
        """验证数据"""
        errors = {}
        
        for field, rules in self.rules.items():
            field_errors = []
            value = data.get(field)
            
            for rule in rules:
                try:
                    # 执行验证
                    if asyncio.iscoroutinefunction(rule.validator):
                        is_valid = await rule.validator(value)
                    else:
                        is_valid = rule.validator(value)
                    
                    if not is_valid:
                        field_errors.append(rule.message)
                        
                except Exception as e:
                    logger.error(f"Validation rule {rule.name} error: {e}")
                    field_errors.append(f"Validation error: {str(e)}")
            
            if field_errors:
                errors[field] = field_errors
        
        return errors
    
    def validate_sync(self, data: Dict[str, Any]) -> Dict[str, List[str]]:
        """同步验证数据"""
        errors = {}
        
        for field, rules in self.rules.items():
            field_errors = []
            value = data.get(field)
            
            for rule in rules:
                try:
                    # 执行验证
                    is_valid = rule.validator(value)
                    
                    if not is_valid:
                        field_errors.append(rule.message)
                        
                except Exception as e:
                    logger.error(f"Validation rule {rule.name} error: {e}")
                    field_errors.append(f"Validation error: {str(e)}")
            
            if field_errors:
                errors[field] = field_errors
        
        return errors
    
    def has_rules(self, field: str) -> bool:
        """检查字段是否有验证规则"""
        return field in self.rules and len(self.rules[field]) > 0


# 全局验证引擎
validation_engine = ValidationEngine()


# 常用验证器
class Validators:
    """常用验证器"""
    
    @staticmethod
    def required(value: Any) -> bool:
        """必填验证"""
        if value is None:
            return False
        
        if isinstance(value, str):
            return len(value.strip()) > 0
        
        return True
    
    @staticmethod
    def min_length(min_len: int):
        """最小长度验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True  # 必填验证由required处理
            
            return len(str(value)) >= min_len
        
        return validator
    
    @staticmethod
    def max_length(max_len: int):
        """最大长度验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            return len(str(value)) <= max_len
        
        return validator
    
    @staticmethod
    def email(value: Any) -> bool:
        """邮箱格式验证"""
        if value is None:
            return True
        
        import re
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return re.match(pattern, str(value)) is not None
    
    @staticmethod
    def numeric(value: Any) -> bool:
        """数字验证"""
        if value is None:
            return True
        
        try:
            float(value)
            return True
        except (ValueError, TypeError):
            return False
    
    @staticmethod
    def min_value(min_val: float):
        """最小值验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            try:
                return float(value) >= min_val
            except (ValueError, TypeError):
                return False
        
        return validator
    
    @staticmethod
    def max_value(max_val: float):
        """最大值验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            try:
                return float(value) <= max_val
            except (ValueError, TypeError):
                return False
        
        return validator
    
    @staticmethod
    def in_range(min_val: float, max_val: float):
        """范围验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            try:
                val = float(value)
                return min_val <= val <= max_val
            except (ValueError, TypeError):
                return False
        
        return validator
    
    @staticmethod
    def enum(values: List[Any]):
        """枚举值验证"""
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            return value in values
        
        return validator
    
    @staticmethod
    def regex(pattern: str):
        """正则表达式验证"""
        import re
        compiled_pattern = re.compile(pattern)
        
        def validator(value: Any) -> bool:
            if value is None:
                return True
            
            return compiled_pattern.match(str(value)) is not None
        
        return validator
    
    @staticmethod
    def async_custom(async_validator):
        """异步自定义验证器"""
        return async_validator


class TransactionIsolation(Enum):
    """事务隔离级别"""
    READ_UNCOMMITTED = "READ UNCOMMITTED"
    READ_COMMITTED = "READ COMMITTED"
    REPEATABLE_READ = "REPEATABLE READ"
    SERIALIZABLE = "SERIALIZABLE"


class TransactionManager:
    """事务管理器"""
    
    def __init__(self, session: AsyncSession):
        self.session = session
        self._savepoints: List[str] = []
        self._nested_level = 0
    
    @asynccontextmanager
    async def transaction(
        self, 
        isolation: Optional[TransactionIsolation] = None,
        rollback_on_error: bool = True
    ):
        """事务上下文管理器"""
        try:
            # 设置隔离级别
            if isolation:
                await self.session.execute(
                    f"SET TRANSACTION ISOLATION LEVEL {isolation.value}"
                )
            
            # 开始事务
            transaction = await self.session.begin()
            
            try:
                yield transaction
                
                # 提交事务
                await transaction.commit()
                
            except Exception as e:
                # 回滚事务
                if rollback_on_error:
                    await transaction.rollback()
                    logger.error(f"Transaction rolled back due to error: {e}")
                else:
                    logger.warning(f"Transaction error occurred but rollback disabled: {e}")
                raise
                
        except Exception as e:
            logger.error(f"Transaction setup error: {e}")
            raise
    
    @asynccontextmanager
    async def savepoint(self):
        """保存点上下文管理器"""
        savepoint_name = f"sp_{self._nested_level}_{id(self)}"
        self._nested_level += 1
        
        try:
            # 创建保存点
            savepoint = await self.session.begin_nested()
            self._savepoints.append(savepoint_name)
            
            yield savepoint
            
            # 释放保存点（提交到父事务）
            await savepoint.commit()
            
        except Exception as e:
            # 回滚到保存点
            logger.warning(f"Rolling back to savepoint {savepoint_name}: {e}")
            await savepoint.rollback()
            raise
        finally:
            self._nested_level -= 1
            if savepoint_name in self._savepoints:
                self._savepoints.remove(savepoint_name)
    
    async def commit(self):
        """提交当前事务"""
        try:
            await self.session.commit()
            logger.debug("Transaction committed successfully")
        except Exception as e:
            logger.error(f"Failed to commit transaction: {e}")
            await self.session.rollback()
            raise DatabaseError(f"Transaction commit failed: {str(e)}")
    
    async def rollback(self):
        """回滚当前事务"""
        try:
            await self.session.rollback()
            logger.debug("Transaction rolled back successfully")
        except Exception as e:
            logger.error(f"Failed to rollback transaction: {e}")
            raise DatabaseError(f"Transaction rollback failed: {str(e)}")
    
    async def execute_in_transaction(
        self,
        func,
        *args,
        isolation: Optional[TransactionIsolation] = None,
        **kwargs
    ):
        """在事务中执行函数"""
        async with self.transaction(isolation=isolation):
            if asyncio.iscoroutinefunction(func):
                return await func(*args, **kwargs)
            else:
                return func(*args, **kwargs)


class DataValidator:
    """数据验证器"""
    
    def __init__(self, schema_class: Type[BaseModel]):
        self.schema_class = schema_class
        self.custom_rules = {}
    
    def add_custom_rule(self, field: str, rule: ValidationRule):
        """添加自定义验证规则"""
        if field not in self.custom_rules:
            self.custom_rules[field] = []
        self.custom_rules[field].append(rule)
    
    async def validate(self, data: Dict[str, Any]) -> tuple[bool, Dict[str, Any], Dict[str, List[str]]]:
        """验证数据"""
        errors = {}
        validated_data = {}
        
        try:
            # Pydantic验证
            schema_instance = self.schema_class(**data)
            validated_data = schema_instance.dict()
            
        except PydanticValidationError as e:
            for error in e.errors():
                field = '.'.join(str(x) for x in error['loc'])
                if field not in errors:
                    errors[field] = []
                errors[field].append(error['msg'])
        
        # 自定义验证
        for field, rules in self.custom_rules.items():
            field_errors = []
            value = data.get(field)
            
            for rule in rules:
                try:
                    if asyncio.iscoroutinefunction(rule.validator):
                        is_valid = await rule.validator(value)
                    else:
                        is_valid = rule.validator(value)
                    
                    if not is_valid:
                        field_errors.append(rule.message)
                        
                except Exception as e:
                    logger.error(f"Custom validation rule {rule.name} error: {e}")
                    field_errors.append(f"Validation error: {str(e)}")
            
            if field_errors:
                if field in errors:
                    errors[field].extend(field_errors)
                else:
                    errors[field] = field_errors
        
        is_valid = len(errors) == 0
        
        return is_valid, validated_data, errors
    
    def validate_sync(self, data: Dict[str, Any]) -> tuple[bool, Dict[str, Any], Dict[str, List[str]]]:
        """同步验证数据"""
        errors = {}
        validated_data = {}
        
        try:
            # Pydantic验证
            schema_instance = self.schema_class(**data)
            validated_data = schema_instance.dict()
            
        except PydanticValidationError as e:
            for error in e.errors():
                field = '.'.join(str(x) for x in error['loc'])
                if field not in errors:
                    errors[field] = []
                errors[field].append(error['msg'])
        
        # 自定义验证
        for field, rules in self.custom_rules.items():
            field_errors = []
            value = data.get(field)
            
            for rule in rules:
                try:
                    is_valid = rule.validator(value)
                    
                    if not is_valid:
                        field_errors.append(rule.message)
                        
                except Exception as e:
                    logger.error(f"Custom validation rule {rule.name} error: {e}")
                    field_errors.append(f"Validation error: {str(e)}")
            
            if field_errors:
                if field in errors:
                    errors[field].extend(field_errors)
                else:
                    errors[field] = field_errors
        
        is_valid = len(errors) == 0
        
        return is_valid, validated_data, errors


class BulkOperationManager:
    """批量操作管理器"""
    
    def __init__(self, session: AsyncSession, batch_size: int = 1000):
        self.session = session
        self.batch_size = batch_size
        self.operation_queue = []
    
    async def add_insert(self, model_class, data: Dict[str, Any]):
        """添加插入操作"""
        self.operation_queue.append(('insert', model_class, data))
        
        if len(self.operation_queue) >= self.batch_size:
            await self.flush()
    
    async def add_update(self, model_instance):
        """添加更新操作"""
        self.operation_queue.append(('update', None, model_instance))
        
        if len(self.operation_queue) >= self.batch_size:
            await self.flush()
    
    async def add_delete(self, model_instance):
        """添加删除操作"""
        self.operation_queue.append(('delete', None, model_instance))
        
        if len(self.operation_queue) >= self.batch_size:
            await self.flush()
    
    async def flush(self):
        """刷新操作队列"""
        if not self.operation_queue:
            return
        
        try:
            for operation, model_class, data in self.operation_queue:
                if operation == 'insert':
                    instance = model_class(**data)
                    self.session.add(instance)
                elif operation == 'update':
                    self.session.merge(data)
                elif operation == 'delete':
                    await self.session.delete(data)
            
            await self.session.flush()
            self.operation_queue.clear()
            
            logger.debug(f"Flushed batch of {self.batch_size} operations")
            
        except Exception as e:
            logger.error(f"Bulk operation flush failed: {e}")
            raise DatabaseError(f"Bulk operation failed: {str(e)}")
    
    async def commit_all(self):
        """提交所有操作"""
        await self.flush()
        await self.session.commit()
        logger.info("All bulk operations committed")


class DataIntegrityChecker:
    """数据完整性检查器"""
    
    def __init__(self, session: AsyncSession):
        self.session = session
    
    async def check_foreign_key(self, table: str, column: str, ref_table: str, ref_column: str) -> Dict[str, Any]:
        """检查外键完整性"""
        try:
            # 查找无效的外键引用
            query = text(f"""
                SELECT COUNT(*) as count
                FROM {table}
                WHERE {column} IS NOT NULL 
                AND {column} NOT IN (SELECT {ref_column} FROM {ref_table})
            """)
            
            result = await self.session.execute(query)
            count = result.scalar()
            
            return {
                "valid": count == 0,
                "invalid_count": count,
                "message": f"Found {count} invalid foreign key references" if count > 0 else "All foreign keys are valid"
            }
            
        except Exception as e:
            logger.error(f"Foreign key integrity check failed: {e}")
            return {
                "valid": False,
                "error": str(e),
                "message": f"Integrity check failed: {str(e)}"
            }
    
    async def check_duplicates(self, table: str, columns: List[str]) -> Dict[str, Any]:
        """检查重复数据"""
        try:
            column_list = ', '.join(columns)
            query = text(f"""
                SELECT {column_list}, COUNT(*) as count
                FROM {table}
                GROUP BY {column_list}
                HAVING COUNT(*) > 1
            """)
            
            result = await self.session.execute(query)
            duplicates = result.fetchall()
            
            return {
                "valid": len(duplicates) == 0,
                "duplicate_count": len(duplicates),
                "duplicates": [dict(row) for row in duplicates],
                "message": f"Found {len(duplicates)} duplicate records" if duplicates else "No duplicates found"
            }
            
        except Exception as e:
            logger.error(f"Duplicate check failed: {e}")
            return {
                "valid": False,
                "error": str(e),
                "message": f"Duplicate check failed: {str(e)}"
            }
    
    async def check_null_constraints(self, table: str, required_columns: List[str]) -> Dict[str, Any]:
        """检查空值约束"""
        try:
            violations = []
            
            for column in required_columns:
                query = text(f"""
                    SELECT COUNT(*) as count
                    FROM {table}
                    WHERE {column} IS NULL
                """)
                
                result = await self.session.execute(query)
                null_count = result.scalar()
                
                if null_count > 0:
                    violations.append({
                        "column": column,
                        "null_count": null_count
                    })
            
            return {
                "valid": len(violations) == 0,
                "violations": violations,
                "message": f"Found {len(violations)} null constraint violations" if violations else "All null constraints satisfied"
            }
            
        except Exception as e:
            logger.error(f"Null constraint check failed: {e}")
            return {
                "valid": False,
                "error": str(e),
                "message": f"Null constraint check failed: {str(e)}"
            }


# 验证装饰器
def validate_data(schema_class: Type[BaseModel]):
    """数据验证装饰器"""
    def decorator(func):
        async def wrapper(*args, **kwargs):
            # 查找数据参数
            data_param = None
            for i, arg in enumerate(args):
                if isinstance(arg, dict):
                    data_param = arg
                    break
            
            if data_param is None:
                # 检查kwargs中的数据
                for key, value in kwargs.items():
                    if isinstance(value, dict):
                        data_param = value
                        break
            
            if data_param is not None:
                validator = DataValidator(schema_class)
                is_valid, validated_data, errors = await validator.validate(data_param)
                
                if not is_valid:
                    raise ValidationError(f"Data validation failed: {errors}")
                
                # 替换原始数据为验证后的数据
                if data_param in args:
                    args = list(args)
                    idx = args.index(data_param)
                    args[idx] = validated_data
                    args = tuple(args)
                else:
                    # 在kwargs中找到并替换
                    for key, value in kwargs.items():
                        if value is data_param:
                            kwargs[key] = validated_data
                            break
            
            if asyncio.iscoroutinefunction(func):
                return await func(*args, **kwargs)
            else:
                return func(*args, **kwargs)
        
        return wrapper
    return decorator
